package com.potevio.simulator.handle;



import com.potevio.simulator.common.GetHexMessage;
import com.potevio.simulator.config.MsgTypeConst;
import com.potevio.simulator.heart.HeartbeatClient;
import com.potevio.simulator.strategy.CommandContext;
import com.potevio.simulator.strategy.ReceiveMsgStrategy;
import com.potevio.simulator.task.SiginMsgTask;
import com.potevio.simulator.util.HexUtil;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Created by haoxy on 2018/10/17.
 * E-mail:hxyHelloWorld@163.com
 * github:https://github.com/haoxiaoyong1014
 * <p>
 * EchoClientHandle继承了 ChannelInboundHandlerAdapter 的一个扩展(SimpleChannelInboundHandler),
 * 而ChannelInboundHandlerAdapter是ChannelInboundHandler的一个实现
 * ChannelInboundHandler提供了可以重写的各种事件处理程序方法
 * 目前，只需继承 SimpleChannelInboundHandler或ChannelInboundHandlerAdapter 而不是自己实现处理程序接口。
 * 我们在这里重写了channelRead0（）事件处理程序方法
 */
public class EchoClientHandle extends Middleware {

    private final static Logger LOGGER = LoggerFactory.getLogger(EchoClientHandle.class);
    /**
     * 用于保存所有Channel对象
     */
    public static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    private HeartbeatClient client;

    public EchoClientHandle(HeartbeatClient client) {
        super("client");
        this.client = client;
    }


    /**
     * 客户端连接服务器后被调用，发送签到报文
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        LOGGER.info("客户端连接服务器，开始发送签到报文——msgLog");
        // 签到定时任务
        SiginMsgTask task = new SiginMsgTask(ctx.channel());
        // 调用定时任务定时发送签到报文（执行线程，初始化延时，执行间隔，计时单位）
        ctx.channel().eventLoop().scheduleAtFixedRate(task, 0, HeartbeatClient.siginInterval, TimeUnit.SECONDS);
    }

    /**
     * 客户端域服务端断开执行重连
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        LOGGER.info("与服务器断开连接，正在重连...");
        super.channelInactive(ctx);
        client.doConnect();
    }

    /**
     * 客户端每隔writerIdleTimeSeconds时间，会检查一次channelRead0方法调用情况，如果writerIdleTimeSeconds
     * 时间段内channelRead方法没有被触发，自动调用userEventTriggered方法
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        // 超时事件
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            // 客户端进入写空闲
            if (idleStateEvent.state() == IdleState.WRITER_IDLE) {
                LOGGER.info("---长期未向服务器发送数据---");
                LOGGER.info("发送心跳报文——msgLog");
                // 生成心跳报文
                Map<String, LinkedHashMap<String, String>> listMap = new GetHexMessage().getDataDomain(MsgTypeConst.HEARTBEAT_MSG);
                // 发送
//                System.out.println("channelId==============" + ctx.channel().id());
                ctx.channel().writeAndFlush(listMap);
            }else if(idleStateEvent.state().equals(IdleState.READER_IDLE)){
                LOGGER.info("---长期未接受到服务器数据---");

            }
        }
        super.userEventTriggered(ctx, evt);
    }

    /**
     * 从服务器接收到数据后调用
     * 每当从服务端接收到新数据时，都会使用收到的消息调用此方法 channelRead0(),在此示例中，接收消息的类型是ByteBuf。
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //从服务端收到消息时被调用
        if (msg instanceof ByteBuf) {
            // 服务端返回的报文
            ByteBuf buf = (ByteBuf) msg;
            String value = ByteBufUtil.hexDump(buf).toUpperCase();
            System.out.println("接收报文==="+value);
            HexUtil.addSpace(value);
            Map<String, LinkedHashMap<String, String>> resultHexMap = HexUtil.parse(value,"2");
            if (resultHexMap != null) {
                Set<String> keySet = resultHexMap.keySet();
                Iterator<String> iterator = keySet.iterator();
                String revertCommandCode = iterator.next();
                if("05".equals(revertCommandCode)){
                    System.out.println("接收0x05");
                }
                CommandContext commandContext = new CommandContext();
                ReceiveMsgStrategy receiveMsgStrategy = commandContext.getReceiveInstance(revertCommandCode);
                if(receiveMsgStrategy != null){
                    receiveMsgStrategy.receiveMessage(ctx, resultHexMap, revertCommandCode);
                }
            }
        }
    }

    /**
     * 与服务端断开连接的操作
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //广播消息给所有的客户端
        channelGroup.writeAndFlush("[服务器] - " + channel.remoteAddress() + " 离开\n");
        LOGGER.info("[服务器] - " + channel.remoteAddress() + " 离开\n");
        ctx.close().sync();
    }

    /**
     * 发生异常时被调用
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LOGGER.error("client exception：", cause.getCause());
        cause.printStackTrace();
        // 释放资源
        ctx.close();
    }


}
